package com.asiainfo.qmc.sonar.core.git;

import com.asiainfo.qmc.sonar.tool.*;
import com.asiainfo.qmc.sonar.core.ToolService;
import com.asiainfo.qmc.sonar.tool.LogCallBack;
import com.asiainfo.qmc.sonar.core.model.ConfConfigure;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.blame.BlameResult;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.errors.AmbiguousObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.transport.*;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class GitNewToolServiceImpl implements ToolService {
    private static final Logger LOGGER = LoggerFactory.getLogger(GitNewToolServiceImpl.class);
    private static final CqmPathDefault.Type CHECK_RULE = CqmPathDefault.Type.SUFFIX_AND_EXSION;
    protected String scmKey;
    protected String gitRoot;
    protected String username;
    protected String password;
    protected String outputDir;
    protected String startPoint;
    protected String sshkeyfileOnlyGit;
    private String startTimeMask = "";
    private String force = "false";
    private String baseSourceDir;
    private String sVersion;
    private String eVersion;
    private String fileName;
    private String fulldose;
    protected String branch;
    private String lastVersion;
    private String lastVersionDate;
    protected ArrayList<String> filePattern;
    protected Map<String, List<String>> gitBlameEntry = new HashMap();
    protected Map<String, List<GitDiff>> gitDiffMap = new HashMap();
    protected Map<String, List<GitBlameData>> gitBlameDatas = new HashMap();
    private ArrayList<RevCommit> incrVers = new ArrayList();
    private Git api;
    private LogCallBack logCallBack;
    DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
//    private List<GitFileObject> curVersionLists = new ArrayList();
//    private List<GitFileObject> diffVersionLists = new ArrayList();
    private boolean isFirst;
    private SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");

    private Map<String, GitObject> gitVersionObjects = new HashMap<>();

    private List<CqmPathDefault> cqmPathRules = new ArrayList<>();

    public GitNewToolServiceImpl() {
    }

    public void setStartTimeMask(String startTimeMask) {
        this.startTimeMask = startTimeMask;
    }

    public void setLogCallBack(LogCallBack logCallBack) {
        this.logCallBack = logCallBack;
    }

    @Override
    public ToolService check(ConfConfigure confConfigure) throws Exception {
        if (null != confConfigure && null != confConfigure.getRepourl() && !"".equalsIgnoreCase(confConfigure.getRepourl()) && null != confConfigure.getOutputDir() && !"".equalsIgnoreCase(confConfigure.getOutputDir()) && null != confConfigure.getScmKey() && !"".equalsIgnoreCase(confConfigure.getScmKey()) && null != confConfigure.getUsername() && null != confConfigure.getPassword() && null != confConfigure.getBranch()) {
            return this;
        } else {
            throw new Exception("配置文件中不存在关键信息: repourl|username|password|outputDir|scmKey|branch, 参数信息为:" + confConfigure.toString());
        }
    }

    private boolean checkBranch(ListMode mode, String branch) throws GitAPIException {
        ListBranchCommand lbc = this.api.branchList();
        boolean find = false;
        List<Ref> refs = null;
        if (null != mode) {
            refs = lbc.setListMode(mode).call();
        } else {
            refs = lbc.call();
        }

        Iterator var6 = refs.iterator();

        while(var6.hasNext()) {
            Ref ref = (Ref)var6.next();
            String name = ref.getName();
            if (name.indexOf("/" + branch) > 0) {
                find = true;
                this.startPoint = name;
                break;
            }
        }

        return find;
    }

    private void checkout(String rM, boolean isBranch) throws GitAPIException, IOException {
        this.checkBranch(ListMode.REMOTE, rM);
        if (isBranch) {
            if (null != this.api.getRepository().resolve(rM)) {
                this.api.checkout().setName(rM).call();
            } else {
                if (null == this.startPoint) {
                    throw new IOException(this.gitRoot + "不存在分支:" + rM);
                }

                this.api.checkout().setCreateBranch(true).setName(rM).setUpstreamMode(SetupUpstreamMode.SET_UPSTREAM).setStartPoint(this.startPoint).call();
            }
        } else {
            try {
                if (null != this.api.getRepository().resolve(rM)) {
                    this.api.reset().setMode(ResetType.HARD).setRef(rM).call();
                }
            } catch (AmbiguousObjectException var4) {
                throw new IOException(this.gitRoot + "分支名无法解析:" + rM);
            } catch (Exception var5) {
                throw new IOException(this.gitRoot + "不存在的版本号:" + rM);
            }
        }

    }
    private void createDir(String path, boolean b) throws Exception {
        File f = new File(path);
        LOGGER.debug("创建目录[" + f.getPath() + "]");
        if (f.exists()) {
            if (!f.isDirectory()) {
                throw new Exception("输出目录" + path + "存在");
            }
        } else {
            if (f.getParentFile().exists()) {
                f.mkdir();
            }else{
                if (f.getParentFile().getParentFile().exists()) {
                    f.getParentFile().mkdir();
                    f.mkdir();
                }else{
                    f.getParentFile().getParentFile().mkdir();
                    f.getParentFile().mkdir();
                    f.mkdir();
                }
            }
        }
    }

    private void createDir(String path) throws Exception {
        File f = new File(path);
        LOGGER.debug("创建目录[" + f.getPath() + "]");
        if (f.exists()) {
            if (!f.isDirectory()) {
                throw new Exception("输出目录" + path + "存在");
            }
        } else {
            f.mkdir();
        }
    }

    private void afterInit() throws Exception {
        File tempRepoPath = new File(this.baseSourceDir);
        FileUtil.traverseDeleteOnly(tempRepoPath + System.getProperty("file.separator") + ".git", ".lock");
//        File tempIndexFile = new File(tempRepoPath + System.getProperty("file.separator") + ".git" + System.getProperty("file.separator") + "index.lock");
//        System.out.println(tempIndexFile);
//        if (tempIndexFile.exists()) {
//            FileUtils.forceDelete(tempIndexFile);
//        }
//        File masterIndexFile = new File(tempRepoPath + System.getProperty("file.separator") + ".git" + System.getProperty("file.separator") + "index.lock");


        if (tempRepoPath.exists()) {
            LOGGER.info("使用已有的本地代码仓库: " + tempRepoPath);
            this.api = Git.open(tempRepoPath);
            if ("true".equalsIgnoreCase(this.fulldose)) {
                this.createDir(this.outputDir + System.getProperty("file.separator") + "code");
                this.createDir(this.outputDir + System.getProperty("file.separator") + "blame");
            }

            this.api.clean().call();
            this.checkout(this.branch, true);
            LOGGER.debug("恢复到最新版本");
            if (this.gitRoot.startsWith("git")) {
                final SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
                    protected JSch createDefaultJSch(FS fs) throws JSchException {
                        JSch defaultJSch = super.createDefaultJSch(fs);
                        if (!"".equalsIgnoreCase(GitNewToolServiceImpl.this.sshkeyfileOnlyGit)) {
                            defaultJSch.removeAllIdentity();
                            defaultJSch.addIdentity(GitNewToolServiceImpl.this.sshkeyfileOnlyGit);
                        }

                        return defaultJSch;
                    }

                    protected void configure(Host host, Session session) {
                        Properties config = new Properties();
                        config.put("StrictHostKeyChecking", "no");
                        session.setConfig(config);
                    }
                };
                ((PullCommand)((PullCommand)this.api.pull().setRebase(true).setRemoteBranchName(this.branch).setCredentialsProvider(new UsernamePasswordCredentialsProvider(this.username, this.password))).setTransportConfigCallback(new TransportConfigCallback() {
                    public void configure(Transport transport) {
                        SshTransport sshTransport = (SshTransport)transport;
                        sshTransport.setSshSessionFactory(sshSessionFactory);
                    }
                })).call();
            } else {
                ((PullCommand)this.api.pull().setRebase(true).setRemoteBranchName(this.branch).setCredentialsProvider(new UsernamePasswordCredentialsProvider(this.username, this.password))).call();
            }

        } else {
            LOGGER.info("新建本地代码仓库: " + tempRepoPath);

            try {
                CloneCommand cmd = Git.cloneRepository();
                cmd.setURI(this.gitRoot);
                cmd.setBranch(this.branch);
                cmd.setNoCheckout(true);
                cmd.setDirectory(tempRepoPath);
                if (this.gitRoot.startsWith("git")) {
                    final SshSessionFactory sshSessionFactory = new JschConfigSessionFactory() {
                        protected JSch createDefaultJSch(FS fs) throws JSchException {
                            JSch defaultJSch = super.createDefaultJSch(fs);
                            if (!"".equalsIgnoreCase(GitNewToolServiceImpl.this.sshkeyfileOnlyGit)) {
                                defaultJSch.setKnownHosts(GitNewToolServiceImpl.this.sshkeyfileOnlyGit);
                            }

                            return defaultJSch;
                        }

                        protected void configure(Host host, Session session) {
                            Properties config = new Properties();
                            config.put("StrictHostKeyChecking", "no");
                            session.setConfig(config);
                        }
                    };
                    cmd.setTransportConfigCallback(new TransportConfigCallback() {
                        public void configure(Transport transport) {
                            SshTransport sshTransport = (SshTransport)transport;
                            sshTransport.setSshSessionFactory(sshSessionFactory);
                        }
                    });
                } else {
                    cmd.setCredentialsProvider(new UsernamePasswordCredentialsProvider(this.username, this.password));
                }

                cmd.call();
            } catch (Exception var6) {
                var6.printStackTrace();
                throw new Exception("仓库" + this.gitRoot + "连接失败或者用户名/密码效验错误" + this.username);
            }

            try {
                this.api = Git.open(tempRepoPath);
                this.api.clean().call();
                this.api.reset().call();
                ((PullCommand)this.api.pull().setCredentialsProvider(new UsernamePasswordCredentialsProvider(this.username, this.password))).setRemoteBranchName(this.branch).call();
                this.checkout(this.branch, true);
            } catch (IOException var5) {
                var5.printStackTrace();
                throw new Exception("clone代码之后无法初始化本地仓库:" + var5.getLocalizedMessage());
            }

            if ("true".equalsIgnoreCase(this.fulldose)) {
                this.createDir(this.outputDir + System.getProperty("file.separator") + "code");
                this.createDir(this.outputDir + System.getProperty("file.separator") + "blame");
            }

            this.createDir(this.outputDir + System.getProperty("file.separator") + "temp");
        }
    }

    private List<String> getBranchs(ListMode mode) throws GitAPIException {
        ListBranchCommand lbc = this.api.branchList();
        List<Ref> refs = null;
        List<String> refNames = new ArrayList();
        if (null != mode) {
            refs = lbc.setListMode(mode).call();
        } else {
            refs = lbc.call();
        }

        Iterator var5 = refs.iterator();

        while(var5.hasNext()) {
            Ref ref = (Ref)var5.next();
            refNames.add(ref.getName());
        }

        return refNames;
    }

    public void init(ConfConfigure confConfigure) throws Exception {
        this.gitRoot = confConfigure.getRepourl();
        this.outputDir = confConfigure.getOutputDir();
        this.force = confConfigure.getForce();
        if (null == this.force || "".equalsIgnoreCase(this.force)) {
            this.force = "false";
        }

        if (null != confConfigure.getBaseSourceDir()) {
            this.baseSourceDir = confConfigure.getBaseSourceDir();
        } else {
            this.baseSourceDir = this.outputDir;
        }

        this.username = confConfigure.getUsername();
        this.password = confConfigure.getPassword();
        this.branch = confConfigure.getBranch();
        this.scmKey = confConfigure.getScmKey();
        this.sVersion = confConfigure.getStartVersion();
        this.fulldose = confConfigure.getFulldose();

        this.sshkeyfileOnlyGit = confConfigure.getSshkeyfileOnlyGit();
        String tempFilePattern = confConfigure.getFilePattern().replaceAll("\\*", "");
        this.filePattern = new ArrayList(Arrays.asList(tempFilePattern.split(",")));
        this.cqmPathRules.stream().forEach(cqmPathRule -> {
            logCallBack.log("--> CqmKey :" + cqmPathRule.getCqmDir().getPrefixCqmDir()
                    +"\t文件过滤[包含]:" + cqmPathRule.getSuffixList()
                    +"\t目录排除:" + cqmPathRule.getExclusionsList());
        });
        this.afterInit();
    }

    public int increment() throws Exception {
        this.checkout(this.branch, true);
        LOGGER.debug("参数信息[起始版本号]-[结束版本号]:[" + (this.sVersion == null ? "" : this.sVersion) + "]-[" + (this.eVersion == null ? "" : this.eVersion) + "]");
        String eVersion = this.getLastVersion();
        String tempVersion = this.getFirstVersion();
        if (StringUtils.isEmpty(this.sVersion)) {
            this.sVersion = tempVersion;
            LOGGER.debug("由于是初次执行，仅获取版本信息!");
            return 1;
        } else {
            LOGGER.debug("修正后[起始版本号]-[结束版本号]:[" + this.sVersion + "]-[" + eVersion + "]");
            if (eVersion.equalsIgnoreCase(this.sVersion)) {
                LOGGER.debug("[起始版本号]-[结束版本号]:[" + this.sVersion + "]-[" + eVersion + "]相同, 已经执行过增量");
                if (!"true".equalsIgnoreCase(this.force)) {
                    return 1;
                }
            }

            this.incrVers.addAll(this.getVersions());

            this.incrVers.stream().forEach(this::getChangeFile);
            return 0;
        }
    }

    private String getLastVersion() {
        try {
            Iterable<RevCommit> commits = this.api.log().setMaxCount(1).call();
            RevCommit commit = null;
            Iterator var3 = commits.iterator();
            if (var3.hasNext()) {
                RevCommit _commit = (RevCommit)var3.next();
                commit = _commit;
            }
            System.out.println("get LasterVersion");

            this.lastVersion = commit.getId().getName();
            this.lastVersionDate = LocalDateTime.ofEpochSecond((long)commit.getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format);
            return commit.getId().getName();
        } catch (Exception var5) {
            return null;
        }
    }

    private String getFirstVersion() {
        try {
            Iterable<RevCommit> commits = this.api.log().setRevFilter(RevFilter.NO_MERGES).call();
            RevCommit commit = null;

            RevCommit _commit;
            for(Iterator var3 = commits.iterator(); var3.hasNext(); commit = _commit) {
                _commit = (RevCommit)var3.next();
            }

            return commit.getId().getName();
        } catch (Exception var5) {
            return "";
        }
    }

    private List<RevCommit> getVersions() {
        String start = sVersion;
        String end = lastVersion;

        List<RevCommit> revs = new ArrayList<>();
        try {
            Iterable<RevCommit> commits = null;
            if (start.equalsIgnoreCase(end) && "true".equalsIgnoreCase(this.fulldose)) {
                commits = this.api.log().setMaxCount(1).call();
            }else {
                commits = this.api.log().addRange(ObjectId.fromString(start), ObjectId.fromString(end)).call();
            }

            Iterator var1 = commits.iterator();
            while(var1.hasNext()) {
                RevCommit rev = (RevCommit) var1.next();
                revs.add(rev);
                if ("true".equalsIgnoreCase(this.fulldose)){
                    return revs;
                }
            }
        } catch (Exception e) {
        }

        return revs;
    }

    private static final String LOCAL_LIST = "LOCAL_LIST";
    private static final String CHANGE_LIST= "CHANGE_LIST";
    private Map<String, List<GitFileObject>> getChangeFile(RevCommit commit){
        Map<String, List<GitFileObject>> result = new HashMap<>();

        Map<String, Integer> dict = new HashMap<>();
        if (commit.getParentCount() == 0){
            LOGGER.debug("这个版本是初始版本"+commit.getId().getName());
        }else{
            result.put(CHANGE_LIST, getChangeFileWithRev(commit, dict));
        }

        if ("true".equalsIgnoreCase(this.fulldose)) {
            result.put(LOCAL_LIST, getLocalFileWithRev(commit, dict));
        }
        try {
            gitVersionObjects.put(commit.getId().getName(), defaultGitObject(commit, result.get(CHANGE_LIST)));
        } catch (GitAPIException e) {
            e.printStackTrace();
        }

        return result;
    }

    private List<GitFileObject> getChangeFileWithRev(RevCommit commit, Map<String, Integer> dict){
        System.out.println("get change" +commit.getId().getName()+":"+commit.getParent(0).getId().getName());
        List<GitFileObject> result = new ArrayList<>();

        AbstractTreeIterator newTree = this.prepareTreeParser(commit);

        AbstractTreeIterator oldTree = this.prepareTreeParser(commit.getParent(0));
        try {
            // .setShowNameAndStatusOnly(true)
            List<DiffEntry> diff = this.api.diff().setOldTree(oldTree).setNewTree(newTree).call();
            String tempFileName = "";
            Iterator var7 = diff.iterator();

            while(var7.hasNext()){
                GitFileObject file;
                DiffEntry diffEntry = (DiffEntry)var7.next();
                file = new GitFileObject();
                if (diffEntry.getChangeType() == ChangeType.ADD) {
                    tempFileName = diffEntry.getNewPath();
                    file.setChangeType(GitFileObject.ChangeType.ADD);
                }else if (diffEntry.getChangeType() == ChangeType.DELETE) {
                    tempFileName = diffEntry.getOldPath();
                    file.setChangeType(GitFileObject.ChangeType.MODIFY);
                }else{
                    tempFileName = diffEntry.getOldPath();
                    file.setChangeType(GitFileObject.ChangeType.MODIFY);
                }
                String finalTempFileName = tempFileName;
                Optional<CqmPathDefault> cprOp = this.cqmPathRules.stream().filter(cqmPathRule -> null != cqmPathRule.check(finalTempFileName, CHECK_RULE)).findFirst();
                if (cprOp.isPresent()){
                    cprOp.get().getCqmDir().configVersion("sonar", LocalDateTime.ofEpochSecond((long)commit.getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format), commit.getId().getName());
                    if (diffEntry.getChangeType() == ChangeType.DELETE) {
                        cprOp.get().getCqmVersion().deleted(commit.getId().getName(), tempFileName);
                    }else {
                        cprOp.get().getCqmVersion().total(commit.getId().getName(), tempFileName);
                    }

                    dict.put(tempFileName, 1);
                    file.setFile(tempFileName);
                }
                result.add(file);
            }
        } catch (GitAPIException e) {
        }


        return result;
    }

    private GitObject defaultGitObject(RevCommit commit, List<GitFileObject> files) throws GitAPIException {
        GitObject gitObject = new GitObject();
        gitObject.setAuthor(commit.getAuthorIdent().getName());
        gitObject.setEmail(commit.getAuthorIdent().getEmailAddress());
        gitObject.setLog(commit.getFullMessage());
        gitObject.setTime(LocalDateTime.ofEpochSecond((long)commit.getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format));
        gitObject.setRevId(commit.getId().getName());
        gitObject.setChangeList(files);
        gitObject.setBranchs(this.getBranchs(ListMode.REMOTE));
        return gitObject;
    }

    private GitFileObject defaultGitFileObject(RevCommit commit, String fileName, GitFileObject.ChangeType changeType) throws GitAPIException {
        GitFileObject gitFileObject = new GitFileObject();
        gitFileObject.setFile(fileName);
        gitFileObject.setChangeType(changeType);
        gitFileObject.setCommitDate(LocalDateTime.ofEpochSecond((long)commit.getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format));
        gitFileObject.setAuthor(commit.getAuthorIdent().getName());
        gitFileObject.setVersion(commit.getId().getName());
        return gitFileObject;
    }

    private List<GitFileObject> getLocalFileWithRev(RevCommit commit, Map<String, Integer> keyMaps) {
        TreeWalk treeWalk = new TreeWalk(this.api.getRepository());
        Throwable var4 = null;
        List<GitFileObject> result = new ArrayList<>();
        try {
            treeWalk.addTree(commit.getTree());
            treeWalk.setRecursive(true);

            while(treeWalk.next()) {
                String tempFileName = treeWalk.getPathString();
                String finalTempFileName = tempFileName;
                Optional<CqmPathDefault> cprOp = this.cqmPathRules.stream().filter(cqmPathRule -> null != cqmPathRule.check(finalTempFileName, CHECK_RULE)).findFirst();
                if (cprOp.isPresent() && !keyMaps.containsKey(tempFileName)){
                    if ("true".equalsIgnoreCase(this.fulldose)){
                        cprOp.get().getCqmVersion().total(commit.getId().getName(), tempFileName);
                    }

                    RevCommit rev = this.api.log().setMaxCount(1).addPath(finalTempFileName).call().iterator().next();

                    GitFileObject file = defaultGitFileObject(rev, finalTempFileName, GitFileObject.ChangeType.ADD);
//                    file.setFile(tempFileName);
//                    file.setChangeType(GitFileObject.ChangeType.ADD);
                    result.add(file);
                }
            }
        } catch (Throwable var15) {
        } finally {
            if (treeWalk != null) {
                if (var4 != null) {
                    try {
                        treeWalk.close();
                    } catch (Throwable var14) {
                        var4.addSuppressed(var14);
                    }
                } else {
                    treeWalk.close();
                }
            }

        }

        return result;
    }

    public int fulldose() throws Exception {
        this.isFirst = this.sVersion == null;
        LOGGER.info("初次执行标识 :" + this.isFirst);
        LOGGER.info("强制执行标识 :" + this.force);
        this.checkout(this.branch, true);
        if (StringUtils.isEmpty(this.sVersion) || "0".equalsIgnoreCase(this.sVersion)) {
            this.sVersion = this.getFirstVersion();
        }
        this.getLastVersion();
        List<RevCommit> revs = this.getVersions();
        if (revs.size() == 0){
            throw new Exception("该仓库没有日志，不需要进行处理!");
        }else {
            Map<String, List<GitFileObject>> fileObjects = this.getChangeFile(revs.get(0));
            if ("true".equalsIgnoreCase(this.force) || !revs.get(0).getId().getName().equalsIgnoreCase(this.lastVersion)) {
                LOGGER.debug("[起始版本号]-[结束版本号]:[" + this.sVersion + "]-[" + revs.get(0).getId().getName() + "]相同, 已经执行过全量, force=true 继续执行");

                List<GitFileObject> changeList = new ArrayList();
                changeList.addAll(fileObjects.get(LOCAL_LIST));
                changeList.addAll(fileObjects.get(CHANGE_LIST));
                this.gitVersionObjects.put( eVersion, this.defaultGitObject(revs.get(0), changeList));
                this.sVersion = revs.get(0).getId().getName();
                return 0;
            } else {
                LOGGER.debug("[起始版本号]-[结束版本号]:[" + this.sVersion + "]-[" + revs.get(0).getId().getName() + "]相同, 已经执行过全量, force!=true 不执行");
                return 1;
            }
        }
    }

    public String getNewStartVersion() {
        return this.lastVersion;
    }

    public String getNewStartVersionDate() {
        return this.lastVersionDate;
    }

    public void log(String startVersion, String endVersion, boolean first) {
    }

    private final static String GIT_VERSION_DELETE = "GIT_VERSION_DELETE";
    private final static String GIT_VERSION_ADD_MODIFY = "GIT_VERSION_ADD_MODIFY";

    private Map<String, List<GitFileObject>> getVersionChange(RevCommit pre, RevCommit cur){
        Map<String, List<GitFileObject>> result = new HashMap<>();
        String curVersion = cur.getId().getName();
        String preVersion = pre.getId().getName();

        List<GitFileObject> deleteFiles = new ArrayList<>();

        LOGGER.debug("比较Pre:"+preVersion+"Cur:"+curVersion);

        logCallBack.log("比较Pre:"+preVersion+"Cur:"+curVersion);
        List<GitFileObject> files = new ArrayList<>();
        // newTree
        AbstractTreeIterator newTree = prepareTreeParser(cur);
        AbstractTreeIterator oldTree = prepareTreeParser(pre);

        List<DiffEntry> diff = null;
        try {
            diff = this.api.diff().setOldTree(oldTree).setNewTree(newTree).setShowNameAndStatusOnly(true).call();
        } catch (GitAPIException e) {

        }
        if (null == diff) {
            logCallBack.log("diff异常:"+pre+":"+cur);
            return null;
        }

        logCallBack.log("变更文件数:"+diff.size());
        for(DiffEntry diffEntry: diff){
            GitFileObject file = new GitFileObject();
            String tempFileName = "";
            if (diffEntry.getChangeType() == ChangeType.ADD){
                logCallBack.log(diffEntry.getPath(DiffEntry.Side.NEW));
                tempFileName = diffEntry.getPath(DiffEntry.Side.NEW);
                file.setChangeType(GitFileObject.ChangeType.ADD);
//                    file.setChangeType(diffEntry.getChangeType());
            }else if (diffEntry.getChangeType() == ChangeType.DELETE){
                tempFileName = diffEntry.getPath(DiffEntry.Side.OLD);

                String finalTempFileName = tempFileName;
                Optional<CqmPathDefault> cprOp = this.cqmPathRules.stream().filter(cqmPathRule -> null != cqmPathRule.check(finalTempFileName, CHECK_RULE)).findFirst();
                if (cprOp.isPresent()){
                    file.setFile(tempFileName);
                    file.setChangeType(GitFileObject.ChangeType.DELETE);
                    deleteFiles.add(file);
                    CqmPathDefault cpr = cprOp.get();

                    cpr.getCqmVersion().deleted(pre.getId().getName() + "", tempFileName.replace(this.baseSourceDir, ""));

                }
                continue;

            }else{
                tempFileName = diffEntry.getPath(DiffEntry.Side.NEW);

                file.setChangeType(GitFileObject.ChangeType.MODIFY);
                //file.setChangeType(diffEntry.getChangeType());
            }
            String finalTempFileName = tempFileName;
            Optional<CqmPathDefault> cprOp = this.cqmPathRules.stream().filter(cqmPathRule -> null != cqmPathRule.check(finalTempFileName, CHECK_RULE)).findFirst();

            if (cprOp.isPresent()) {
                logCallBack.log("符合的文件:"+file.toString());
                file.setFile(tempFileName);
                files.add(file);
                CqmPathDefault cpr = cprOp.get();

                cpr.getCqmVersion().total(pre.getId().getName() + "", tempFileName.replace(this.baseSourceDir, ""));
            }else{
                logCallBack.log("不符合的文件:"+tempFileName);
                continue;
            }

        }
        result.put(GIT_VERSION_DELETE, deleteFiles);
        result.put(GIT_VERSION_ADD_MODIFY, files);
        return result;
    }

    public int incrBlame() throws Exception {
        this.cqmPathRules.stream().forEach(cqmPathDefault -> {
            cqmPathDefault.process("true".equalsIgnoreCase(this.fulldose),
                    new CqmCallback() {
                        @Override
                        public Boolean svn() {
                            return false;
                        }

                        @Override
                        public int getThreadNum() {
                            return 10;
                        }

                        @Override
                        public void svnBlame(String version, String path, String file, CqmStoreFile cqmStoreFile) throws Exception {
                        }

                        @Override
                        public List<String> blameData(String version, String file) throws Exception{
                            BlameResult result = api.blame().setStartCommit(api.getRepository().resolve(version)).setFilePath(file).call();

                            List<String> tempBlameInfo = new ArrayList();
                            String versionAuthor = "";
                            GitBlameData gitData = null;
                            for (int i = 0; i < result.getResultContents().size(); ++i) {
                                RevCommit commit = result.getSourceCommit(i);

                                if ("".equalsIgnoreCase(versionAuthor)) {
                                    gitData = new GitBlameData();
                                    gitData.setAuthor(commit.getAuthorIdent().getEmailAddress());
                                    gitData.setAuthor_version(commit.getId().getName());
                                    try (RevWalk revWalk = new RevWalk(api.getRepository())) {
                                        RevCommit _commit = revWalk.parseCommit(commit);
                                        gitData.setAuthor_date(_commit.getAuthorIdent().getWhen());
                                    }
                                    versionAuthor = commit.getId().getName()+commit.getAuthorIdent().getName();
                                }else if (!versionAuthor.equalsIgnoreCase(commit.getId().getName()+commit.getAuthorIdent().getName())){
                                    gitData = new GitBlameData();
                                    gitData.setAuthor(commit.getAuthorIdent().getEmailAddress());
                                    gitData.setAuthor_version(commit.getId().getName());
                                    try (RevWalk revWalk = new RevWalk(api.getRepository())) {
                                        RevCommit _commit = revWalk.parseCommit(commit);

                                        gitData.setAuthor_date(_commit.getAuthorIdent().getWhen());
                                    }
                                    versionAuthor = commit.getId().getName()+commit.getAuthorIdent().getName();
                                }


                                tempBlameInfo.add((i + 1) + " " + commit.getId().getName() + " " + df.format(gitData.getAuthor_date()) + " " + commit.getAuthorIdent().getName() + " " + commit.getAuthorIdent().getEmailAddress());
                            }
                            return tempBlameInfo;
                        }

                        @Override
                        public String dateTime(String version) {
                            Optional<RevCommit> op = incrVers.stream().filter(rev -> version.equalsIgnoreCase(rev.getId().getName())).findAny();
                            if (op.isPresent()){
                                return LocalDateTime.ofEpochSecond((long)op.get().getCommitTime(), 0, ZoneOffset.ofHours(8)).format(format);
                            }else{
                                return "";
                            }
                        }

                        @Override
                        public CqmOper gitObject(String version) {
                            return gitVersionObjects.get(version);
                        }
                    }
            );
        });

//        versionNum = incrVers.size()-1;
//        String os = System.getProperty("os.name");
//        sVersion = getFirstVersion();
//        LOGGER.info("需要处理的版本列表数量为:"+(incrVers.size()-1));
//        logCallBack.log("需要处理的版本列表数量为:"+(incrVers.size()-1));
//
//        for(int i = 0; i < incrVers.size()-1; i++){
//            Map<String, List<GitFileObject>> changeMap = this.getVersionChange(incrVers.get(i+1), incrVers.get(i));
//
//            defaultGitObject(incrVers.get(i), changeMap.get(GIT_VERSION_ADD_MODIFY));
//            eVersion = incrVers.get(i).getId().getName();
//            if (gitVersionObjects.get(eVersion).getChangeList().size() > 0) {
//
//                String tempOutputDir = this.outputDir + System.getProperty("file.separator") + "sonar." + LocalDateTime.ofEpochSecond(incrVers.get(i+1).getCommitTime(), 0, ZoneOffset.ofHours(8)).format(format) + "." + eVersion;
//                createDir(tempOutputDir);
//                blame();
//                // blame之后把delete的文件再加回来
//                gitVersionObjects.get(eVersion).getChangeList().addAll(changeMap.get(GIT_VERSION_DELETE));
//                createDir(tempOutputDir + System.getProperty("file.separator") + "code");
//                createDir(tempOutputDir + System.getProperty("file.separator") + "blame");
////                exportFile(tempOutputDir);
//                exportBlameFile(tempOutputDir);
//            }else{
//                gitVersionObjects.get(eVersion).getChangeList().addAll(changeMap.get(GIT_VERSION_DELETE));
//                if (gitVersionObjects.get(eVersion).getChangeList().size() > 0){
//                    String tempOutputDir = this.outputDir + System.getProperty("file.separator") + "sonar." + LocalDateTime.ofEpochSecond(incrVers.get(i+1).getCommitTime(), 0, ZoneOffset.ofHours(8)).format(format) + "." + eVersion;
//                    createDir(tempOutputDir, true);
//                    this.exportInfo(tempOutputDir, "log");
//                }else {
//                    LOGGER.debug("版本号" + eVersion + "无有效变更文件,不处理");
//                }
//
//            }
//        }
        LOGGER.debug("reset"+this.eVersion);
        this.api.reset().setMode(ResetType.HARD).setRef(this.eVersion).call();

        return 0;
    }

    public int blame() throws Exception {
        return incrBlame();
    }
    public int blame1111() throws Exception {
//        LOGGER.info("需要获取blame信息的文件数量为:" + this.gitObject.getChangeList().size() + "个");
//        tFileNum += Long.valueOf(this.gitObject.getChangeList().size());
//        LOGGER.info("处理的版本号:"+this.gitObject.getRevId());
//        logCallBack.log("处理的版本号:"+this.gitObject.getRevId());
        ObjectId commitID = this.api.getRepository().resolve(this.gitVersionObjects.get(this.eVersion).getRevId());
        this.api.reset().setMode(ResetType.HARD).setRef(this.gitVersionObjects.get(this.eVersion).getRevId()).call();
        BlameCommand blameCommand = this.api.blame();

        Iterator var3 = this.gitVersionObjects.get(this.eVersion).getChangeList().iterator();

        List<GitFileObject> newChangeList = new ArrayList<>();

        while(var3.hasNext()) {
            GitFileObject change = (GitFileObject)var3.next();
            String file = change.getFile();

            BlameResult result = blameCommand.setStartCommit(commitID).setFilePath(file).call();

            // 全量时 获取最新的实际版本号
            if ("true".equalsIgnoreCase(this.fulldose)) {
                Iterable<RevCommit> commits = this.api.log().addPath(file).setMaxCount(1).call();

                RevCommit lastCommit = null;
//                logCallBack.log("全量 文件的最新版本号:" + commitID.getName());
                for(RevCommit _commit:commits) {
                    lastCommit = _commit;
                    break;
                }
//                logCallBack.log("全量 文件的最新实际版本号:" + commitID.getName());
                if (null != lastCommit) {
                    change.setVersion(lastCommit.getName());
                    change.setAuthor(lastCommit.getAuthorIdent().getName());
                    change.setCommitDate(LocalDateTime.ofEpochSecond((long)lastCommit.getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format));
                }
            }

            List<String> tempBlameInfo = new ArrayList();
//            List<String> codes = this.countLinesOfFileInCommit(this.api.getRepository(), commitID, file);
            String versionAuthor = "";
            GitBlameData gitData = null;
            for (int i = 0; i < result.getResultContents().size(); ++i) {
                RevCommit commit = result.getSourceCommit(i);

                if ("".equalsIgnoreCase(versionAuthor)) {
                    gitData = new GitBlameData();
                    gitData.setAuthor(commit.getAuthorIdent().getEmailAddress());
                    gitData.setAuthor_version(commit.getId().getName());
                    try (RevWalk revWalk = new RevWalk(this.api.getRepository())) {
                        RevCommit _commit = revWalk.parseCommit(commit);
                        gitData.setAuthor_date(_commit.getAuthorIdent().getWhen());
                    }
                    versionAuthor = commit.getId().getName()+commit.getAuthorIdent().getName();
                }else if (!versionAuthor.equalsIgnoreCase(commit.getId().getName()+commit.getAuthorIdent().getName())){
                    gitData = new GitBlameData();
                    gitData.setAuthor(commit.getAuthorIdent().getEmailAddress());
                    gitData.setAuthor_version(commit.getId().getName());
                    try (RevWalk revWalk = new RevWalk(this.api.getRepository())) {
                        RevCommit _commit = revWalk.parseCommit(commit);
                        gitData.setAuthor_date(_commit.getAuthorIdent().getWhen());
                    }
                    versionAuthor = commit.getId().getName()+commit.getAuthorIdent().getName();
                }

                tempBlameInfo.add((i + 1) + " " + commit.getId().getName() + " " + this.df.format(gitData.getAuthor_date()) + " " + commit.getAuthorIdent().getName() + " " + commit.getAuthorIdent().getEmailAddress());
            }

            newChangeList.add(change);
            this.gitBlameEntry.put(file, tempBlameInfo);
        }
        this.gitVersionObjects.get(this.eVersion).getChangeList().clear();
        this.gitVersionObjects.get(this.eVersion).getChangeList().addAll(newChangeList);

        if ("true".equalsIgnoreCase(this.fulldose)) {
            versionNum++;
            this.exportBlameFile(this.outputDir);
        }

        return 0;
    }

    @Override
    public void addCqmPathRules(List<CqmPathDefault> cqmPathRule) {
        this.cqmPathRules.addAll(cqmPathRule);
    }

    private void exportBlameFile(String path) {
//        this.exportFile(path);
//    test
//        this.exportInfo(path, "log");
//        this.exportInfo(path, "blame");
        if (null != this.gitBlameEntry) {
            this.gitBlameEntry.clear();
        }

    }

    public String getSVersion() {
        return this.sVersion;
    }

    public AbstractTreeIterator prepareTreeParser(RevCommit commit) {
        try {
            RevWalk walk = new RevWalk(this.api.getRepository());
            Throwable var3 = null;

            CanonicalTreeParser var37;
            try {
                RevTree tree = walk.parseTree(commit.getTree().getId());
                CanonicalTreeParser oldTreeParser = new CanonicalTreeParser();
                ObjectReader oldReader = this.api.getRepository().newObjectReader();
                Throwable var7 = null;

                try {
                    oldTreeParser.reset(oldReader, tree.getId());
                } catch (Throwable var32) {
                    var7 = var32;
                    throw var32;
                } finally {
                    if (oldReader != null) {
                        if (var7 != null) {
                            try {
                                oldReader.close();
                            } catch (Throwable var31) {
                                var7.addSuppressed(var31);
                            }
                        } else {
                            oldReader.close();
                        }
                    }

                }

                walk.dispose();
                var37 = oldTreeParser;
            } catch (Throwable var34) {
                var3 = var34;
                throw var34;
            } finally {
                if (walk != null) {
                    if (var3 != null) {
                        try {
                            walk.close();
                        } catch (Throwable var30) {
                            var3.addSuppressed(var30);
                        }
                    } else {
                        walk.close();
                    }
                }

            }

            return var37;
        } catch (Exception var36) {
            return null;
        }
    }

    public void log() throws Exception {
        List<RevCommit> commitList = new ArrayList();
        Iterable<RevCommit> commits = this.api.log().setRevFilter(RevFilter.NO_MERGES).setMaxCount(2).call();
        Iterator commitIt = commits.iterator();

        while(commitIt.hasNext()) {
            commitList.add((RevCommit)commitIt.next());
        }

        List<GitFileObject> files = new ArrayList();
        if (commitList.size() == 0) {
            throw new Exception("该仓库没有日志，不需要进行处理!");
        } else {
            Throwable _oldTree;
            if (commitList.size() == 1) {
                TreeWalk treeWalk = new TreeWalk(this.api.getRepository());
                _oldTree = null;

                try {
                    treeWalk.addTree(((RevCommit)commitList.get(0)).getTree());
                    treeWalk.setRecursive(true);

                    while(treeWalk.next()) {
                        GitFileObject file = new GitFileObject();
                        file.setFile(treeWalk.getPathString());
                        file.setChangeType(GitFileObject.ChangeType.ADD);
                        files.add(file);
                    }
                } catch (Throwable var17) {
                    _oldTree = var17;
                    throw var17;
                } finally {
                    if (treeWalk != null) {
                        if (_oldTree != null) {
                            try {
                                treeWalk.close();
                            } catch (Throwable var16) {
                                _oldTree.addSuppressed(var16);
                            }
                        } else {
                            treeWalk.close();
                        }
                    }

                }
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            DiffFormatter df = new DiffFormatter(out);
            df.setRepository(this.api.getRepository());
            if (commitList.size() == 2) {
                AbstractTreeIterator newTree = prepareTreeParser(commitList.get(0));
                AbstractTreeIterator oldTree = prepareTreeParser(commitList.get(1));
                List<DiffEntry> diff = this.api.diff().setOldTree(oldTree).setNewTree(newTree).setShowNameAndStatusOnly(true).call();

                GitFileObject file;
                for(Iterator var9 = diff.iterator(); var9.hasNext(); files.add(file)) {
                    DiffEntry diffEntry = (DiffEntry)var9.next();
                    file = new GitFileObject();
                    if (diffEntry.getChangeType() == ChangeType.ADD) {
                        file.setFile(diffEntry.getNewPath());
                        file.setChangeType(GitFileObject.ChangeType.ADD);
                    } else if (diffEntry.getChangeType() == ChangeType.DELETE) {
                        file.setFile(diffEntry.getOldPath());
                        file.setChangeType(GitFileObject.ChangeType.DELETE);
                    } else {
                        file.setFile(diffEntry.getOldPath());
                        file.setChangeType(GitFileObject.ChangeType.MODIFY);
                    }
                }
            }

//
//            this.gitObject = new GitObject();
//            this.gitObject.setAuthor(((RevCommit)commitList.get(0)).getAuthorIdent().getName());
//            this.gitObject.setEmail(((RevCommit)commitList.get(0)).getAuthorIdent().getEmailAddress());
//            this.gitObject.setLog(((RevCommit)commitList.get(0)).getFullMessage());
//            this.gitObject.setTime(LocalDateTime.ofEpochSecond((long)((RevCommit)commitList.get(0)).getCommitTime(), 0, ZoneOffset.ofHours(8)).format(this.format));
//            this.gitObject.setRevId(((RevCommit)commitList.get(0)).getId().getName());
//            this.gitObject.setChangeList(files);
//            this.gitObject.setBranchs(this.getBranchs(ListMode.REMOTE));
        }
    }

//    private void exportFile(String path) {
//        LOGGER.debug("移动文件数量" + this.gitObject.getChangeList().size());
//        if (this.gitObject.getChangeList().size() == 0){
//            return;
//        }
//        LOGGER.debug("移动code文件(源):" + this.baseSourceDir);
//        LOGGER.debug("移动code文件(目标):" + path + System.getProperty("file.separator") + "code");
//        FileUtil.moveFile(this.baseSourceDir, path + System.getProperty("file.separator") + "code", (Map)this.gitObject.getChangeList().stream().collect(Collectors.toMap(GitFileObject::getFile, GitFileObject::getChangeTypeStr)));
//    }

//    public void exportInfo(String path, String type) {
//        String os = System.getProperty("os.name");
//        if ("log".equalsIgnoreCase(type)) {
//            if (null == this.gitObject) {
//                LOGGER.info("没有需要导出的文件日志!");
//                return;
//            }
//
//            LOGGER.debug("**输出COMMITINFO文件: revId:" + this.gitObject.getRevId());
//            logCallBack.log("**输出COMMITINFO文件: revId:" + this.gitObject.getRevId());
//            if (!new File(path).exists()){
//                try {
//                    createDir(path, true);
//                } catch (Exception e) {
//                }
//            }
//
//            FileUtil.writeObject(this.gitObject, path + System.getProperty("file.separator") + "COMMITINFO");
//
//        }
//
//        if ("blame".equalsIgnoreCase(type)) {
//            if (null == this.gitBlameEntry) {
//                LOGGER.info("没有需要导出的文件日志!");
//                return;
//            }
//
//            Iterator var10 = this.gitBlameEntry.entrySet().iterator();
//
//            while(var10.hasNext()) {
//                Entry<String, List<String>> entry = (Entry)var10.next();
//                String fileName = "";
//                String tempFileName = (String)entry.getKey();
//                String tempPath = "";
//                int index = StringUtils.lastIndexOf(tempFileName, "/");
//                if (index == -1) {
//                    fileName = tempFileName;
//                } else {
//                    fileName = StringUtils.substring(tempFileName, index + 1);
//                    tempPath = StringUtils.substring(tempFileName, 0, index);
//                }
//
//                if (os.startsWith("win") || os.startsWith("Win")) {
//                    tempPath = tempPath.replaceAll("/", "\\\\");
//                }
//
//                LOGGER.debug("输出Blame文件: revId:" + this.gitObject.getRevId());
//                LOGGER.debug("输出Blame文件: fileName:" + fileName);
//                LOGGER.debug("输出Blame文件位置: Path:" + path + System.getProperty("file.separator") + "blame" + System.getProperty("file.separator") + tempPath + System.getProperty("file.separator"));
//                FileUtil.writeFile(path + System.getProperty("file.separator") + "blame" + System.getProperty("file.separator") + tempPath + System.getProperty("file.separator"), fileName, StringUtils.join((Iterable)entry.getValue(), "\n") + "\n");
//            }
//        }
//    }

    @Override
    public List<CqmPathDefault> getCqmPathRules() {
        return null;
    }

    private long versionNum = 0L; // 此次执行处理的版本数
    private long tFileNum = 0L;   // 此次执行检测的文件数

    public void display() {
        logCallBack.log("检测文件数: "+this.tFileNum);
        this.cqmPathRules.stream().forEach(cqmPathDefault -> {
            logCallBack.log("本次执行的sonarKey: "+cqmPathDefault.getCqmDir().getPrefixCqmDir());
            logCallBack.log("\t有效版本数: "+ cqmPathDefault.getCqmVersion().getVersionList().size());
            logCallBack.log("\t"+"处理文件数: "+cqmPathDefault.getCqmVersion().size(CqmVersion.TOTAL));
            logCallBack.log("成功文件数: "+cqmPathDefault.getCqmVersion().size(CqmVersion.SUCCESS)+"\t"+"失败文件数: "+cqmPathDefault.getCqmVersion().size(CqmVersion.FAILED));
            logCallBack.log("处理删除的文件数: "+cqmPathDefault.getCqmVersion().size(CqmVersion.DELETED));
        });



    }

    @Override
    public boolean packState() {
        if ("true".equalsIgnoreCase(this.fulldose)){
            return versionNum > 0;
        }
        return this.cqmPathRules.stream().filter(cqmPathRule -> !cqmPathRule.getCqmVersion().isZore()).findAny().isPresent();
    }

    @Override
    public void exportInfo(String path, String type) throws Exception {

    }
}
